extends Node

onready var sproto = Sproto.new()
onready var connection = StreamPeerTCP.new()
onready var request_callbacks = {}
onready var response_callbacks = {}
onready var buffer = PoolByteArray()
onready var connectStatus = StreamPeerTCP.STATUS_NONE


# Called when the node enters the scene tree for the first time.
func _ready():
	set_process(false)
	buffer.resize(0)
	var f = File.new()
	if (f.open("res://protocol.spb", File.READ)) == OK:
		var bytes = f.get_buffer(f.get_len())
		sproto.newsproto(bytes)
		f.close()
		sproto.host("Header")

#recv some bytes
func _try_recv_data():
	var result = connection.get_partial_data(1024)
	if result.empty() or result[0] != OK:
		return false
	var data = result[1]
	if data.size() > 0:
		for i in range(data.size()):
			buffer.push_back(data[i])
	return true

#delete some element in the head
func _pop_buffer(size):
	for i in range(size, buffer.size()):
		buffer[i-size] = buffer[i]
	buffer.resize(buffer.size()-size)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	if connection.get_status() == StreamPeerTCP.STATUS_ERROR:
		_on_connection_error()
		return
	if connection.get_status() == StreamPeerTCP.STATUS_NONE and connectStatus == StreamPeerTCP.STATUS_CONNECTED:
		_on_connection_closed()
		return
	if connection.get_status() == StreamPeerTCP.STATUS_CONNECTED and connectStatus == StreamPeerTCP.STATUS_CONNECTING:
		_on_connection_established()
	while true:
		if !_try_recv_data():
			break
		if buffer.size() < 2:
			break
		var size = buffer[0] * 256 + buffer[1]
		if buffer.size() < size + 2:
			return
		var bytes = PoolByteArray()
		for i in range(size):
			bytes.push_back(buffer[2+i])
		_pop_buffer(size+2)
		_on_data_received(bytes)
	
func _on_connection_established():
	set_process(true)
	connectStatus = StreamPeerTCP.STATUS_CONNECTED
	var msg = "connection open from tcp"
	if not response_callbacks.has("net.on_open"):
		return
	for cbInfo in response_callbacks["net.on_open"]:
		var callback = cbInfo.callback
		if callback != null:
			callback.call_func(msg)
	


func _on_connection_closed():
	set_process(false)
	connectStatus = StreamPeerTCP.STATUS_NONE
	var msg = "connection close from tcp"
	if not response_callbacks.has("net.on_close"):
		return
	for cbInfo in response_callbacks["net.on_close"]:
		var callback = cbInfo.callback
		if callback != null:
			callback.call_func(msg)
	
func _on_connection_error():
	set_process(false)
	connectStatus = StreamPeerTCP.STATUS_NONE
	var msg = "connection error from tcp"
	if not response_callbacks.has("net.on_error"):
		return
	for cbInfo in response_callbacks["net.on_error"]:
		var callback = cbInfo.callback
		if callback != null:
			callback.call_func(msg)
		
	

func _on_data_received(bytes):
	var result = sproto.dispatch(bytes)
	if result == null:
		return
	if result["type"] == Sproto.NONE:
		return
	var method = result["protocol"]
	var data = null
	if result.has("data"):
		data = result["data"]
	var ud = null
	if result.has("ud"):
		ud = result["ud"]
	if result["type"] == Sproto.REQUEST:
		var session = 0
		if result.has("session"):
			session = result["session"]
		if not request_callbacks.has(method):
			return
		for cbInfo in request_callbacks[method]:
			var callback = cbInfo.callback
			if callback != null:
				callback.call_func(session, data, ud)
		return
	elif result["type"] == Sproto.RESPONSE:
		if not response_callbacks.has(method):
			return
		for cbInfo in response_callbacks[method]:
			var callback = cbInfo.callback
			if callback != null:
				callback.call_func(data, ud)
		


#connect_server("127.0.0.1", 8080)
func connect_server(host, port):
	if connection.get_status() == StreamPeerTCP.STATUS_CONNECTED:
		disconnect_server()
	var err = connection.connect_to_host(host, port)
	if err != OK:
		set_process(false)
	else:
		set_process(true)
	if connection.get_status() == StreamPeerTCP.STATUS_CONNECTING:
		connectStatus = StreamPeerTCP.STATUS_CONNECTING
	return err
	


func disconnect_server():
	connection.disconnect_from_host()
	
func is_connecting_to_host():
	return connection.get_status() == StreamPeerTCP.STATUS_CONNECTING

func is_connected_to_host():
	return connection.get_status() == StreamPeerTCP.STATUS_CONNECTED
	
# request("auth.login", req, ud)
func request(method, req, ud=null):
	var bytes = sproto.request(method, req, ud)
	var pbytes = PoolByteArray()
	pbytes.resize(0)
	pbytes.push_back(bytes.size() >> 8 & 0xFF)
	pbytes.push_back(bytes.size() & 0xFF)
	pbytes.append_array(bytes)
	var err = connection.put_data(pbytes)
	if err != OK:
		_on_connection_error()
	return err
	
# register_response("auth.login", obj, "login")
func register_request(method, obj, funcname):
	var callback = funcref(obj, funcname)
	if not request_callbacks.has(method):
		request_callbacks[method] = []
	for cbInfo in request_callbacks[method]:
		if cbInfo.obj == obj:
			return
	request_callbacks[method].push_back({ "obj":obj, "callback":callback })
	return
	
# unregister_event("auth.login", obj)
func unregister_request(method, obj):
	if not request_callbacks.has(method):
		return
	for i in range(request_callbacks[method].size()):
		if request_callbacks[method][i].obj == obj:
			request_callbacks[method].remove(i)
			return
	return
	

# register_response("auth.login", obj, "on_login")
func register_response(method, obj, funcname):
	var callback = funcref(obj, funcname)
	if not response_callbacks.has(method):
		response_callbacks[method] = []
	for cbInfo in response_callbacks[method]:
		if cbInfo.obj == obj:
			return
	response_callbacks[method].push_back({ "obj":obj, "callback":callback })
	return
	
# unregister_response("auth.login", obj)
func unregister_response(method, obj):
	if not response_callbacks.has(method):
		return
	for i in range(response_callbacks[method].size()):
		if response_callbacks[method][i].obj == obj:
			response_callbacks[method].remove(i)
			return
	return
	



